// ltc_demo.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_BLOCKS 64
#define BUF_LENGTH 256*1024
#define MAX_DEGREE 5

typedef struct _block_t_{
	char *buf;
	int degree;//if the current block is original block degree=1, index[0]=index of message block
	int index[MAX_DEGREE];
	struct _block_t_ *next;
}block_t;

typedef struct _lct_context_t_{
	block_t *pending;
	int *block_index;
}lct_context_t;


char g_msg[MAX_BLOCKS * BUF_LENGTH];
int decode(lct_context_t *context, block_t *io_block);

/* in encode, the io_block parameter
* as input p, the block info store the input message index and degree*/
int ltc_encode(char *in_buf[], block_t *io_block)
{
	int i,j;
	unsigned int *p[MAX_DEGREE];
	unsigned int *op=(unsigned int *)io_block->buf;
	/* in order to speed up XOR operation, use word instead of byte*/
	for(i=0;i<io_block->degree;i++)
	{
		p[i]=(unsigned int *)in_buf[i];

	}
	/*XOR each word to output buffer*/
	for(j=0;j<(BUF_LENGTH>>2);j++)
	{
		op[j]=0;
		for(i=0;i<io_block->degree;i++)
			op[j]^=p[i][j];
	}
	return 1;
}

/* right now, we just return global buffer pointer, 
* but in actual operation, first check the block is arrived or not
*if arrived check data in cache, if not found get block from stored file
*or maybe from sqlite:)
*/ 
char *get_block_data(lct_context_t *context, int index)
{
	int offset=index>>5; /*div by 32*/
	int flag=0x1L<<(index % 32);

	if(index>MAX_BLOCKS)
		return NULL;

	if((context->block_index)[offset] & flag)
	{
		/* which says we have downloaded block data */
		return &g_msg[index * BUF_LENGTH];
	}
	else
		return NULL;
}

/* right now just put the data to g_msg
* in future, we should manage it by sqlite 
*if the file is very large, the memory occupy will un unbearable*/
int put_block_data(lct_context_t * context, block_t *io_block)
{
	if(io_block)
	{
		memcpy(&g_msg[io_block->index[0]* BUF_LENGTH],io_block->buf,BUF_LENGTH);
	}
	return 0;
}

/* return value:
* 0: discard block, no need decode
* 1: decodable
* -1: input block error
* other value: not decodable
*/
int check_decodable(lct_context_t *context, block_t *b)
{
	int i;
	int degree=0;
	if(b!=NULL)
	{
		for(i=0;i<b->degree;i++)
		{
			int offset=b->index[i]>>5; /*div by 32*/
			int flag=0x1L<<(b->index[i] % 32);
			
			if(!((context->block_index)[offset] & flag))
			{
				/* if not in block*/
				degree++;
			}
			if(degree >1)
				break;
		}
		return degree;
	}else
		return -1;

}

/* free block*/
void free_block(block_t *b)
{
	if(b!=NULL)
	{
		if(b->buf)
			free(b->buf);
		free(b);
	}
}

/* check the context pending buffer and decode if it is decodable*/
int check_do_context(lct_context_t *context)
{
	block_t *b=context->pending;
	/*check if all index data has arrived, just free the block*/
	while(b !=NULL)
	{
		int degree=check_decodable(context, context->pending);

		if(degree == 0)
		{
			/* remove the block and continue*/
			free_block(context->pending);
			context->pending=context->pending->next;
			b=context->pending;
		}else if(degree ==1)
		{
			/* decode the block*/
			decode(context,b);
			put_block_data(context,b);
			free_block(b);
			/*free the block*/
			free_block(context->pending);
			context->pending=context->pending->next;
			b=context->pending;
		}else if(degree >1)
		{
			/* check to see if the next if decodable*/
			b=b->next;
		}
	}
	return 0;
}


/* if not decodable, append to context pending queue, else deocde*/
int ltc_decode(lct_context_t *context, block_t *io_block)
{
	/* if the block is original message
	* get the data and set context index flag
	*/
	if(io_block->degree ==1) 
	{
		int offset=io_block->index[0]>>5; /*div by 32*/
		int flag=0x1L<<(io_block->index[0] % 32);
		context->block_index[offset] |= flag;
		/*store data*/
		put_block_data(context,io_block);
		free_block(io_block);
		/* since we add the new block we should check to see if new packet is decodable*/
		check_do_context(context);
	}else{
		int c=check_decodable(context,io_block);

		if(c==0)
			free_block(io_block);
		else if(c==1)
		{
			/* decodable, decode and store data */
			decode(context,io_block);
			/*store data*/
			put_block_data(context,io_block);
			free_block(io_block);
			/* since we add the new block we should check to see if new packet is decodable*/
			check_do_context(context);			
		}else{
			/*append it to the context pending queue*/
			io_block->next=context->pending;
			context->pending=io_block;
		}
	}
	return 0;
}

/* decode the io_block, and set the context index flag
*the result is stored in place of io_block->buf
*/
int decode(lct_context_t *context, block_t *io_block)
{
	int i,j;
	unsigned int *op=(unsigned int *)io_block->buf;
	unsigned int *data;
	for(i=0;i<io_block->degree;i++)
	{
		int offset=io_block->index[i]>>5; /*div by 32*/
		int flag=0x1L<<(io_block->index[i] % 32);
		
		if(!((context->block_index)[offset] & flag))
		{
			/* if not in block, set context and do the next*/
			context->block_index[offset] |= flag;
			continue;
		}else{
			/*XOR the data with io_block->buf*/
			data=(unsigned int *)get_block_data(context, io_block->index[i]);
			for(j=0;j<(BUF_LENGTH>>2);j++)
			{
				op[j]^=data[j];
			}
		}
	}
	return 0;
}

int main(int argc, char* argv[])
{
	return 0;
}
